<template>
   <div class="hello">
       <div style="text-align: center;">
        id：<span>{{fromIm}}</span>
        </div>
        <div style="text-align: center;">
            对方id：<input type="text" name="" v-model="toIm">
            <button @click="sender">拨打</button>
        </div>
        <div class="remote-main">
          <video class="video remote" id="remoteVideo" autoplay playsinline webkit-playsinline
              x5-video-player-type="h5"
              x5-video-player-fullscreen="true"
              x5-video-orientation="portraint" 
          ></video>
        </div>
        <div class="local-main">
          <!-- muted静音   playsinline-->
          <video muted class="video local" id="localVideo" autoplay playsinline webkit-playsinline
              x5-video-player-type="h5"
              x5-video-player-fullscreen="true"
              x5-video-orientation="portraint" 
          ></video>
        </div>
   </div>
</template>   
<script>
  export default {
    components: {},
    data() {
      return {
        fromIm: Math.floor(Math.random()*1000),
        toIm: '',
        ws: '',
        rtcPeer: '',
        localVideo: '',
        remoteVideo: '',
        offerIces: [],
        answerIces: [],
        iceFlag: false
      }
    },
    mounted() {
      this.initWs()
    },
    methods:{
      sender(){
        if(!this.toIm){
            alert("toIm不能为空")
            return;
        }
        this.initMedia('offer')
      },
      initWs(){
        let _this = this
        //修改成你自己websocket服务端的ip和端口
        _this.ws = new WebSocket("wss://www.xsport.site/ws?username="+this.fromIm);
        _this.ws.onopen = function(){
            // Web Socket 已连接上，使用 send() 方法发送数据
            alert("WebSocket连接成功");
            //心跳检测
            setInterval(() => {
                _this.ws.send("{msgType: 'PING'}")
            },5000)
        };
        //我自己定义的格式，你们可以根据自己修改
        //ret = {msgType: 'RTC',msg: '消息体',toIm: '接收人', fromIm: '发送人'}
        _this.ws.onmessage = function (ret){ 
            var data = JSON.parse(ret.data)
            console.log("收到消息",data)
            if(data.msgType!=='RTC'){
            return;
            }
            const { type, sdp, iceCandidate } = JSON.parse(data.msg)
            console.log("收到消息",type,iceCandidate)
            if (type === 'answer') {
                _this.rtcPeer.setRemoteDescription(new RTCSessionDescription({ type, sdp }));
            } else if (type === 'answer_ice') {
                _this.answerIces.push(iceCandidate)
            } else if (type === 'offer') {
                _this.toIm = data.fromIm
                _this.initMedia("answer",new RTCSessionDescription({ type, sdp }));
            } else if (type === 'offer_ice') {
                _this.offerIces.push(iceCandidate)
            }
        };
        
        _this.ws.onclose = function(){ 
            // 关闭 websocket
            alert("WebSocket连接已关闭..."); 
        };
      },
      //接收拨打方的消息，判断后添加候选人（因为addIceCandidate必须在设置描述remoteDescription之后，如果还没设置描述，我们先把它存起来，添加描述后再添加候选人）
      intervalAddIce(){
        let _this = this
        setInterval(() => {
          if(_this.rtcPeer && _this.rtcPeer.remoteDescription && _this.rtcPeer.remoteDescription.type){
            if(!_this.iceFlag){
              _this.iceFlag = true;
              while(_this.offerIces.length>0){
                let iceCandidate = _this.offerIces.shift();
                _this.rtcPeer.addIceCandidate(iceCandidate).then(_=>{
                  console.log("success addIceCandidate()");
                }).catch(e=>{
                  console.log("Error: Failure during addIceCandidate()",e);
                });
              }

              while(_this.answerIces.length>0){
                let iceCandidate = _this.answerIces.shift();
                _this.rtcPeer.addIceCandidate(iceCandidate).then(_=>{
                  console.log("success addIceCandidate()");
                }).catch(e=>{
                  console.log("Error: Failure during addIceCandidate()",e);
                });
              }
              _this.iceFlag = false;
            }
          }
        }, 1000);
      },
      //初始化媒体源
      async initMedia(iceType,offerSdp){
        var _this = this
        //ios浏览器不判断这部分会提示不支持  (navigator.mediaDevices && navigator.mediaDevices.getUserMedia)
        const UserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia || (navigator.mediaDevices && navigator.mediaDevices.getUserMedia);
        if(!UserMedia){
          alert("media,您的浏览器不支持访问用户媒体设备，请换一个浏览器")
          return;
        }
        //RTCPeerConnection 接口代表一个由本地计算机到远端的WebRTC连接。该接口提供了创建，保持，监控，关闭连接的方法的实现。   
        const PeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection;
        if(!PeerConnection){
          alert("peer,您的浏览器不支持访问用户媒体设备，请换一个浏览器")
          return;
        }

        _this.localVideo = document.getElementById('localVideo');

        navigator.mediaDevices.getUserMedia({
          audio: {
            channelCount: {ideal: 2,min: 1}, //双声道
            echoCancellation: true, //回声消除
            autoGainControl: true, //修改麦克风输入音量，自动增益
            noiseSuppression: true //消除背景噪声
          }
          // video: { //可以指定获取传入摄像头影像的宽高
          //   width: 400,
          //   height: 500
          // }
          ,video: true
        }).then(async stream => {
          await _this.initPeer(iceType,PeerConnection)
          _this.intervalAddIce()
          //成功打开音视频流
          try {
            _this.localVideo.srcObject = stream;
          } catch (error) {
            _this.localVideo.src = await window.URL.createObjectURL(stream);
          }
          stream.getTracks().forEach( async track => {
           await _this.rtcPeer.addTrack(track, stream);
          });
          if (!offerSdp) {
            console.log('创建本地SDP');
            const offer = await _this.rtcPeer.createOffer();
            await _this.rtcPeer.setLocalDescription(offer);
            
            console.log(`传输发起方本地SDP`,offer);
            await _this.ws.send(_this.getMsgObj(offer));

          } else {
            console.log('接收到发送方SDP');
            await _this.rtcPeer.setRemoteDescription(offerSdp);

            console.log('创建接收方（应答）SDP');
            const answer = await _this.rtcPeer.createAnswer();
            console.log(`传输接收方（应答）SDP`);
            await _this.ws.send(_this.getMsgObj(answer));
            await _this.rtcPeer.setLocalDescription(answer);
          }
        }).catch(error => {
          alert("无法开启本地媒体源："+error);
        })
      },
      //初始化webRtc连接
      async initPeer(iceType,PeerConnection){
        let _this = this
        _this.remoteVideo = document.getElementById('remoteVideo');

        if(!_this.rtcPeer){
          var stun = "stun:120.24.202.127:3478"
          var turn = "turn:120.24.202.127:3478"
          var peerConfig = {
              "iceServers": [{
                  "urls": stun
              }, {
                  "urls": turn,
                  "username": "admin",
                  "credential": "123456"
              }]
          };
          //如果是局域网，不需要配置穿透服务，直接传null即可
          _this.rtcPeer = new PeerConnection(peerConfig);
          _this.rtcPeer.onicecandidate = async (e) => {
            if (e.candidate) {
              console.log("搜集并发送候选人")
              await _this.ws.send(_this.getMsgObj({
                type: iceType+'_ice',
                iceCandidate: e.candidate
              }));
            }else{
              console.log("候选人收集完成")
            }
          };
          _this.rtcPeer.ontrack =  async(e) => {
            console.log("ontrack",e)
              if (e && e.streams) {
                _this.remoteVideo.srcObject = await e.streams[0];
              }
          };
        }
      },
      getMsgObj(msg){
        var msgObj = {
          fromIm: this.fromIm,
          toIm: this.toIm,
          msg: msg,
          msgType: "RTC",
        }
        console.log(msgObj)
        return JSON.stringify(msgObj);
      }
    }
  }   
</script>

<style scoped>
  .video {
     transform: rotateY(180deg);
   }
   .remote{
     float: left;
   }
   .local{

   }
</style>   